using System;
using System.Collections.Generic;
using System.Configuration;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace SBPweb.Logging
{
    public static partial class LogManager
    {
        private static List<EntLibTraceListener> mUninitializedListeners = new List<EntLibTraceListener>();
        private static List<EntLibTraceListener> mInitializedListeners = new List<EntLibTraceListener>();

        private static List<EntLibTextFormatter> mUninitializedTextFormatters = new List<EntLibTextFormatter>();

		private static LoggingOptionsSection mLoggingOptionsSection = null;

        private static volatile bool mInitialized = false;

        /// <summary>
        /// Gets if the logger system is initialized or not.
        /// </summary>
        public static bool Initialized
        {
            get
            {
                return mInitialized;
            }
        }

		/// <summary>
		/// Gets if the Global Implicit Categories are disabled or not. 
		/// </summary>
		public static Boolean DisableGlobalImplicitCategories
		{
			get
			{
				if (mLoggingOptionsSection == null)
				{
					return false;
				}
				return mLoggingOptionsSection.DisableGlobalImplicitCategories;
			}
		}

        /// <summary>
        /// Initializes the Enterprise Library logging application block.
        /// </summary>
        /// <remarks>
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <param name="ConfigurationSource">Source of logging configuration.</param>
        /// <exception cref="ConfigurationErrorsException">Object building failed while configuration evaulation.</exception> 
        private static void InitializeSubsystem(String ConfigurationSource)
        {
			IConfigurationSource configSource = ConfigurationSource != null ? ConfigurationSourceFactory.Create(ConfigurationSource) : ConfigurationSourceFactory.Create();
			LogWriterFactory factory = new LogWriterFactory(configSource);
			mLogWriter = factory.Create();
			mLoggingOptionsSection = (LoggingOptionsSection)configSource.GetSection("loggingOptions");
			if (mLoggingOptionsSection != null)
			{
				mLoggingOptionsSection.ApplySeverityFilters();
			}
        }

        /// <summary>
        /// Initializes the Enterprise Library logging application block from the specified configuration source.
        /// </summary>
        /// <remarks>
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <param name="ConfigurationSource">Source of <see cref="LogManager"/> configuration.</param>
        private static void LoadConfiguration(String ConfigurationSource)
        {
            /*
            IConfigurationSource configsource = ConfigurationSource != null ? ConfigurationSourceFactory.Create(ConfigurationSource) : ConfigurationSourceFactory.Create();

            ConfigurationSection configsection = configsource.GetSection("LogManagerConfigSection");
            */
        }

        /// <summary>
        /// Registers a trace listener as uninitialized.
        /// </summary>
        /// <remarks>
        /// One has to call <see cref="LogManager.Initialize()"/> to initialize them.
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <param name="TraceListener">The trace listener to be registered.</param>
        internal static void AddUninitializedListener(EntLibTraceListener TraceListener)
        {
            lock (mUninitializedListeners)
            {
                mUninitializedListeners.Add(TraceListener);
            }
        }

        /// <summary>
        /// Removes a trace listener from the list of initialized (active) trace listeners.
        /// </summary>
        /// <remarks>
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <param name="TraceListener">The trace listener to be removed.</param>
        internal static void RemoveInitializedListener(EntLibTraceListener TraceListener)
        {
            lock (mInitializedListeners)
            {
                mInitializedListeners.Remove(TraceListener);
            }
        }

        /// <summary>
        /// Registers a text formatter as uninitialized.
        /// </summary>
        /// <remarks>
        /// One has to call <see cref="LogManager.Initialize()"/> to initialize them.
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <param name="Formatter">The trace listener to be registered.</param>
        internal static void AddUninitializedTextFormatter(EntLibTextFormatter Formatter)
        {
            lock (mUninitializedTextFormatters)
            {
                mUninitializedTextFormatters.Add(Formatter);
            }
        }

        /// <summary>
        /// Initializes the trace listeners those are registered as uninitialized.
        /// </summary>
        /// <remarks>
        /// All trace listener instances of classes deriving from <see cref="EntLibTraceListener"/> will be registered on instantiation.
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        private static void InitializeTraceListeners()
        {
            lock (mUninitializedListeners)
            {
                foreach (EntLibTraceListener eltl in mUninitializedListeners)
                    eltl.InitializeRelatedObjects();

                lock (mInitializedListeners)
                    mInitializedListeners.AddRange(mUninitializedListeners);

                mUninitializedListeners.Clear();
            }
        }

		/// <summary>
		/// Returns initialized trace listeners
		/// </summary>
		/// <returns>List of initialized trace listeners</returns>
		public static List<EntLibTraceListener> GetInitializedTraceListeners()
		{
			lock (mInitializedListeners)
			{
				List<EntLibTraceListener> ret = new List<EntLibTraceListener>();
				foreach (EntLibTraceListener listener in mInitializedListeners)
				{
					ret.Add(listener);
				}
				return ret;
			}
		}

        /// <summary>
        /// Initializes the text formatters those are registered as uninitialized.
        /// </summary>
        /// <remarks>
        /// All formatter instances of classes deriving from <see cref="EntLibTextFormatter"/> will be registered on instantiation.
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        private static void InitializeTextFormatters()
        {
            lock (mUninitializedTextFormatters)
            {
                foreach (EntLibTextFormatter eltf in mUninitializedTextFormatters)
                    eltf.InitializeRelatedObjects();

                mUninitializedTextFormatters.Clear();
            }
        }

        /// <summary>
        /// Entry point for the user to initialize EntLib logging framework from the default configuration source.
        /// </summary>
        /// <remarks>
        /// If the default configuration source does not contain information about EntLib logging framework then it will explicitly
        /// build up a default framework.
        /// !!! We still did not specified the default framework so it is just a theoritical way of behavior.
        /// </remarks>
        /// <exception cref="ConfigurationErrorsException">Object building failed while configuration evaulation.</exception> 
        public static void Initialize()
        {
            Initialize(null);
        }

        /// <summary>
        /// Terminates the EntLib logging framework on exiting the process.
        /// </summary>
        /// <param name="Sender">Caller of event.</param>
        /// <param name="Args">Event arguments.</param>
        private static void OnProcessExit(Object Sender, EventArgs Args)
        {
            Terminate();
        }

        /// <summary>
        /// Writes one last log message about an unhandled exception and then Terminates the EntLib logging framework on exiting the process.
        /// </summary>
        /// <param name="Sender">Caller of event.</param>
        /// <param name="Args">Event arguments.</param>
        private static void OnUnhandledException(Object Sender, UnhandledExceptionEventArgs Args)
        {
            WriteCritical(Args.ExceptionObject);

            Terminate();
        }


        /// <summary>
        /// Entry point for the user to initialize EntLib logging frameworkn from a specified configuration source.
        /// </summary>
        /// <remarks>
        /// If the default configuration source does not contain information about EntLib logging framework then it will explicitly
        /// build up a default framework.
        /// !!! We still did not specified the default framework so it is just a theoritical way of behavior.
        /// </remarks>
        /// <param name="ConfigurationSource">Name of the configuration source to use.</param>
        /// <exception cref="ConfigurationErrorsException">Object building failed while configuration evaulation.</exception> 
        public static void Initialize(String ConfigurationSource)
        {
            lock (typeof(LogManager))
            {
                // Terminate previous logging session
                Terminate();

                // Initialize subsystem
                InitializeSubsystem(ConfigurationSource);

                // Initialize instances
                InitializeTraceListeners();
                InitializeTextFormatters();

                // Load configuration informations
                LoadConfiguration(ConfigurationSource);

                AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit);
                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);

                mInitialized = true;
            }
        }

        /// <summary>
        /// Entry point for the user to Terminate EntLib logging framework.
        /// </summary>
        public static void Terminate()
        {
            lock (typeof(LogManager))
            {
                if (!mInitialized)
                    return;

                mInitialized = false;

                AppDomain.CurrentDomain.ProcessExit -= new EventHandler(OnProcessExit);
                AppDomain.CurrentDomain.UnhandledException -= new UnhandledExceptionEventHandler(OnUnhandledException);

                if (mLogWriter != null)
                {
                    mLogWriter.Dispose();
                    mLogWriter = null;
                }

                lock (mUninitializedListeners)
                {
                    mUninitializedListeners.Clear();
                }

                lock (mUninitializedTextFormatters)
                {
                    mUninitializedTextFormatters.Clear();
                }

                lock (mInitializedListeners)
                {
                    foreach (EntLibTraceListener eltl in mInitializedListeners)
                    {
                        eltl.Close();
                    }

                    mInitializedListeners.Clear();
                }

                lock (mCategorySeverityFilters)
                {
                    mCategorySeverityFilters.Clear();
                }

                GlobalImplicitCategories = null;

                lock (mImplicitCategories)
                {
                    mImplicitCategories.Clear();
                }

                lock (mKeywords)
                {
                    mKeywords.Clear();
                }

				if (mLoggingOptionsSection != null)
				{
					lock (mLoggingOptionsSection)
					{
						mLoggingOptionsSection.Terminate();
					}
				}
            }
        }

        /// <summary>
        /// Called by <see cref="LogFolder"/> to determine which file based trace listener holds a handle on a file that should be archived.
        /// </summary>
        /// <remarks>
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <param name="FilePath">The path of the locked filed.</param>
        /// <returns>Returns the file trace listener which is associated with the given file path or null if no such listener exists</returns>
        internal static EntLibFileTraceListener GetEntLibFileTraceListener(String FilePath)
        {
            lock (mInitializedListeners)
            {
                foreach (EntLibTraceListener eltl in mInitializedListeners)
                {
                    if (eltl is EntLibFileTraceListener)
                    {
                        EntLibFileTraceListener elftl = eltl as EntLibFileTraceListener;

                        if (elftl.FilePath == FilePath)
                            return elftl;
                    }
                }
            }

            return null;
        }
    }
}
